package top.hmtools.wxmp.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import top.hmtools.wxmp.core.annotation.WxmpController;
import top.hmtools.wxmp.core.annotation.WxmpMessage;
import top.hmtools.wxmp.core.annotation.WxmpRequestMapping;
import top.hmtools.wxmp.core.model.MessageMetaInfo;
import top.hmtools.wxmp.core.model.message.BaseMessage;
import top.hmtools.wxmp.core.model.message.TempMessage;
import top.hmtools.wxmp.core.model.message.enums.Event;
import top.hmtools.wxmp.core.model.message.enums.EventKey;
import top.hmtools.wxmp.core.model.message.enums.MsgType;

/**
 * 核心的微信消息（事件）处理工具
 * FIXME <br>试验版，后期可能需要改良
 * @author HyboWork
 *
 */
public class DefaultWxmpMessageHandle {
	
	private final Logger logger = LoggerFactory.getLogger(DefaultWxmpMessageHandle.class);
	
	/**
	 * 消息类型与消息元数据字典容器
	 */
	private HashMap<MsgType,MessageMetaInfo> msgTypeMap = new HashMap<>();
	
	/**
	 * 事件类型与消息元数据字典容器
	 */
	private HashMap<Event	,MessageMetaInfo> eventMap = new HashMap<>();
	
	/**
	 * 事件类型key与消息元数据字典容器
	 */
	private HashMap<EventKey,MessageMetaInfo> eventKeyMap = new HashMap<>();

	/**
	 * 解析并添加微信xml消息处理器
	 * <br>必须是被{@link top.hmtools.wxmp.core.annotation.WxmpController}注解修饰的类
	 * <br>其中的处理方法必须是被 {@link top.hmtools.wxmp.core.annotation.WxmpRequestMapping}注解修饰
	 * <br>其中处理方法只能有一个形式参数，且该形式参数的类必须继承自 {@link top.hmtools.wxmp.core.model.message.BaseMessage}，且该类必须被注解 {@link top.hmtools.wxmp.core.annotation.WxmpMessage} 注解修饰
	 * @param obj
	 */
	public void addMessageMetaInfo(Object obj){
		//检查参数是否为null
		if(obj == null){
			this.logger.error("入参为null，忽略……");
			return;
		}
		
		//检查obj是否被 top.hmtools.wxmp.core.annotation.WxmpController 注解修饰，没有则打印日志并忽略（或者抛出运行时异常？？）
		WxmpController wxmpController = obj.getClass().getAnnotation(WxmpController.class);
		if(wxmpController == null){
			throw new RuntimeException(obj.getClass()+"没有被注解："+WxmpController.class+"修饰");
		}
		
		//提取该对象实例中所有被 top.hmtools.wxmp.core.annotation.WxmpRequestMapping 注解修饰的方法
		Method[] methods = obj.getClass().getMethods();
		
		for(Method method:methods){
			WxmpRequestMapping wxmpRequestMapping = method.getAnnotation(WxmpRequestMapping.class);
			if(wxmpRequestMapping != null){
				//将提取出的 方法反射 信息 组装成 top.hmtools.wxmp.core.model.MessageMetaInfo 对象实例
				MessageMetaInfo messageMetaInfo = new MessageMetaInfo();
				messageMetaInfo.setMethod(method);
				messageMetaInfo.setObj(obj);
				
				//提取方法 及其 形式参数
				Class<?>[] parameterTypes = method.getParameterTypes();
				if(parameterTypes == null || parameterTypes.length<1){
					throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"没有形参");
				}
				
				if(!BaseMessage.class.isAssignableFrom(parameterTypes[0])){
					throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterTypes[0]+"没有继承"+BaseMessage.class);
				}
				Class<? extends BaseMessage> parameterType = (Class<? extends BaseMessage>) parameterTypes[0];
				messageMetaInfo.setMessageBeanClass(parameterType);
				WxmpMessage wxmpMessage = parameterType.getAnnotation(WxmpMessage.class);
				if(wxmpMessage == null){
					throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterType+"没有被注解："+WxmpMessage.class+"修饰");
				}
				
				//检查容器里是否已存在，已存在则抛出运行时异常（不能同时存在2个以上处理同一种消息结构的方法），不存在则存入容器
				MsgType msgType = wxmpMessage.msgType();
				if(msgType == null){
					throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterType+"的注解："+WxmpMessage.class+"没有设置MsgType值");
				}
				
				//FIXME 先检查是否有 eventKey 前缀，有则处理。本处还需要进一步观察
				EventKey eventKey = wxmpMessage.eventKey();
				if(eventKey != null && !eventKey.equals(EventKey.none)){
					//检查消息类型是不是 event
					if(!msgType.equals(MsgType.event)){
						throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterType+"的注解："+WxmpMessage.class+"设置了EventKey，但没有设置MsgType值为Event类型");
					}
					//检查event是否有值
					Event event = wxmpMessage.event();
					if(StringUtils.isEmpty(event.getName())){
						throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterType+"的注解："+WxmpMessage.class+"设置了EventKey，但没有设置Event值");
					}
					//检查是否已注册
					if(this.eventKeyMap.containsKey(eventKey)){
						this.logger.error("当前：{}",method);//当前
						this.logger.error("已有：{}",this.eventKeyMap.get(eventKey).getMethod());//已有
						throw new RuntimeException("存在多个EventKey值为："+eventKey+"的消息元数据");
					}
					this.eventKeyMap.put(eventKey, messageMetaInfo);
					continue;
				}
				
				//如果消息类型是  事件 
				if(msgType.equals(MsgType.event)){
					Event event = wxmpMessage.event();
					if(StringUtils.isEmpty(event.getName())){
						throw new RuntimeException(obj.getClass()+"的method："+method.getName()+"的形式参数："+parameterType+"的注解："+WxmpMessage.class+"没有设置Event值");
					}
					if(this.eventMap.containsKey(event)){
//						this.logger.error("当前：{}",parameterType);//当前
//						this.logger.error("已有：{}",this.eventMap.get(event).getMethod().getParameterTypes()[0]);//已有
						this.logger.error("当前：{}",method);//当前
						this.logger.error("已有：{}",this.eventMap.get(event).getMethod());//已有
						throw new RuntimeException("存在多个Event值为："+event+"的消息元数据");
					}
					this.eventMap.put(event, messageMetaInfo);
					continue;
				}
				
				if(this.msgTypeMap.containsKey(msgType)){
//					this.logger.error("{}",parameterType);//当前
//					this.logger.error("{}",this.msgTypeMap.get(msgType).getMethod().getParameterTypes()[0]);//已有
					this.logger.error("当前：{}",method);//当前
					this.logger.error("已有：{}",this.msgTypeMap.get(msgType).getMethod());//已有
					throw new RuntimeException("存在多个MsgType值为："+msgType+"的消息元数据");
				}
				
				this.msgTypeMap.put(msgType, messageMetaInfo);
			}
		}
		
	}
	
	/**
	 * 解析并处理从微信服务器接收到的xml字符串数据
	 * @param xml
	 * @return
	 */
	public Object processXmlData(String xml){
		if(this.logger.isDebugEnabled()){
			this.logger.debug("处理xml消息，xml原文是：{}",xml);
		}
		//检查入参
		if(StringUtils.isEmpty(xml)){
			throw new IllegalArgumentException("入参为空");
		}
		
		//转化成临时消息实体对象实例
		TempMessage tempMessage  = new TempMessage();
		tempMessage.loadDataFromXml(xml);
		
		//根据消息类型、事件类型从容器中提取出对应得处理方法
		MsgType msgType = tempMessage.getMsgType();
		if(msgType == null){
			throw new RuntimeException("该消息中没有MsgType元素值");
		}
		MessageMetaInfo messageMetaInfo = null;
		if(msgType.equals(MsgType.event)){
			//事件
			Event event = tempMessage.getEvent();
			if(event == null){
				throw new RuntimeException("该事件消息中没有Event元素值");
			}
			
			//FIXME 是否有eventKey前缀。关于 eventKey 的处理，还有待观察
			String eventKey = tempMessage.getEventKey();
			if(StringUtils.isNotEmpty(eventKey)){
				//消息中含有eventKey节点信息
				Set<EventKey> eventKeySet = this.eventKeyMap.keySet();
				for(EventKey item:eventKeySet){
					//遍历eventKey注册字典，查看是否存在当前消息eventKey值前缀的注册信息 FIXME 是否要转换成大写进行检查？？
					boolean isStartsWith = eventKey.startsWith(item.getName());
					if(isStartsWith){
						//如果当前消息的eventKey的前缀包括在eventKey注册字典中
						messageMetaInfo = this.eventKeyMap.get(item);
						break;
					}
				}
				
				//遍历完 eventKey 注册字典，虽然当前消息eventKey节点有值，但没有被注册特有前缀，故认为应在event注册字典中
				if(messageMetaInfo == null){
					messageMetaInfo = this.eventMap.get(event);
				}
			}else{
				//eventKey 为空，则取eventmap中数据
				messageMetaInfo = this.eventMap.get(event);
			}
		}else{
			//非事件
			messageMetaInfo = this.msgTypeMap.get(msgType);
		}
		
		if(messageMetaInfo == null){
			this.logger.error("无法解析该xml消息：{}",xml);
			return null;
		}
		
		//通过反射机制执行后，返回其返回的结果数据
		Class<? extends BaseMessage> messageBeanClass = messageMetaInfo.getMessageBeanClass();
		try {
			BaseMessage baseMessage = messageBeanClass.newInstance();
			baseMessage.loadDataFromXml(xml);
			return messageMetaInfo.getMethod().invoke(messageMetaInfo.getObj(), baseMessage);
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}


	public Logger getLogger() {
		return logger;
	}


	/**
	 * 消息类型与消息元数据字典容器
	 * @return
	 */
	public HashMap<MsgType, MessageMetaInfo> getMsgTypeMap() {
		return msgTypeMap;
	}

	/**
	 * 事件类型与消息元数据字典容器
	 * @return
	 */
	public HashMap<Event, MessageMetaInfo> getEventMap() {
		return eventMap;
	}
}
